package grame

import grails.gorm.DetachedCriteria
import org.apache.shiro.SecurityUtils
import javax.validation.constraints.NotNull

import static grame.ResourcePermission.ANY_RESOURCE_ID
import static grame.ResourcePermission.PERMISSION_TARGET_RESOURCE

@SuppressWarnings("GroovyUnusedDeclaration")
class DomainPermissionsService {

    static boolean impliesPermission(@NotNull Class domainClass, Serializable domainId, String operationType) {
        return impliesPermission(domainClass, domainId, operationTypeString2List(operationType))
    }

    static boolean impliesPermission(@NotNull Class domainClass, Serializable domainId = ANY_RESOURCE_ID, @NotNull Collection operationTypes) {
        String domainName = domainClass.simpleName
        return SecurityUtils.subject.isPermitted(ResourcePermission.toPermission(domainName, domainId, operationTypes.join(',')))
    }

    static List<Serializable> impliesIdList(@NotNull Class domainClass, String operationType) {
        return impliesIdList(domainClass, operationTypeString2List(operationType))
    }

    static List<Serializable> impliesIdList(@NotNull Class domainClass, @NotNull Collection operationTypes) {
        String domainName = domainClass.simpleName
        String principal = SecurityUtils.subject.principal as String
        // First find all the permissions that the user has that match
        // the required permission's type and project code.
        def permissions = []
        // Get the permissions that the user does have.
        permissions += User.executeQuery("select distinct p from Admin as user join user.permissions as p where user.username = '$principal' and p like '${PERMISSION_TARGET_RESOURCE}:${domainName}:%' or p = '${PERMISSION_TARGET_RESOURCE}:*'") ?: []
        // Get the permissions from the roles that the user does have.
        permissions += User.executeQuery("select distinct p from Admin as user join user.roles as role join role.permissions as p where user.username = '$principal' and p like '${PERMISSION_TARGET_RESOURCE}:${domainName}:%' or p = '${PERMISSION_TARGET_RESOURCE}:*'") ?: []
        // 获取拥有权限的所有id
        return ResourcePermission.findIdCollectionFromPermissionsByResourceNameAndOperationTypes(permissions, domainName, operationTypes)
    }

    static List impliesDomainList(@NotNull Class domainClass, @NotNull String operationType, Map queryParams) {
        return impliesDomainList(domainClass, operationTypeString2List(operationType), queryParams)
    }

    static List impliesDomainList(@NotNull Class domainClass, @NotNull Collection operationTypes, Map queryParams) {
        def idList = impliesIdList(domainClass, operationTypes)
        return _getDomainInstanceList(domainClass, idList, queryParams)
    }

    protected static List _getDomainInstanceList(Class domainClass, Collection idList, Map queryParams = [:], Closure additionalQuery = null) {
        AppSetting.where {}
        def instanceList = []
        if (idList) {
            DetachedCriteria query = domainClass.invokeMethod('where', {
                if (!idList.contains('*'))
                    inList('id', idList)
            } as Closure) as DetachedCriteria
            if (additionalQuery) {
                query = query.build(additionalQuery)
            }
            instanceList = query.list(queryParams)
        }
        return instanceList
    }

    protected static operationTypeString2List(String operationType) {
        return operationType ? [operationType] : ['create', 'read', 'update', 'delete']
    }
}
